
<?php
namespace ApprovalFlow;

use think\Model;
use think\facade\Db;

class ApprovalFlow
{
    // 审批流状态常量
    const STATUS_PENDING = 0;
    const STATUS_APPROVED = 1;
    const STATUS_REJECTED = 2;
    const STATUS_CANCELLED = 3;

    protected $config = [];

    public function __construct(array $config = [])
    {
        $this->config = array_merge($this->getDefaultConfig(), $config);
    }

    protected function getDefaultConfig()
    {
        return [
            'db_prefix' => 'approval_',
            'message_callback' => null,
        ];
    }

    /**
     * 创建新的审批流程
     */
    public function createFlow(array $flowData): int
    {
        // 验证流程数据
        $this->validateFlowData($flowData);

        // 保存流程主表数据
        $flowId = Db::name($this->config['db_prefix'].'flow')
            ->insertGetId([
                'title' => $flowData['title'],
                'creator_id' => $flowData['creator_id'],
                'status' => self::STATUS_PENDING,
                'created_at' => time(),
                'updated_at' => time(),
            ]);

        // 保存审批节点
        $this->saveNodes($flowId, $flowData['nodes']);

        return $flowId;
    }

    /**
     * 保存审批节点
     */
    protected function saveNodes(int $flowId, array $nodes): void
    {
        foreach ($nodes as $index => $node) {
            $nodeId = Db::name($this->config['db_prefix'].'nodes')
                ->insertGetId([
                    'flow_id' => $flowId,
                    'name' => $node['name'],
                    'type' => $node['type'] ?? 2, // 默认个人
                    'condition' => $node['condition'] ?? null,
                    'sort' => $index,
                ]);

            // 保存审批人
            $this->saveApprovers($nodeId, $node['approvers'] ?? []);

            // 保存抄送人
            if (!empty($node['cc'])) {
                $this->saveCC($flowId, $node['cc'], $nodeId);
            }
        }
    }

    /**
     * 保存审批人
     */
    protected function saveApprovers(int $nodeId, array $approvers): void
    {
        foreach ($approvers as $approver) {
            Db::name($this->config['db_prefix'].'approvers')
                ->insert([
                    'node_id' => $nodeId,
                    'approver_type' => $approver['type'] ?? 2,
                    'approver_id' => $approver['id'],
                ]);
        }
    }

    /**
     * 保存抄送人
     */
    protected function saveCC(int $flowId, array $ccList, ?int $nodeId = null): void
    {
        foreach ($ccList as $cc) {
            Db::name($this->config['db_prefix'].'cc')
                ->insert([
                    'flow_id' => $flowId,
                    'cc_type' => $cc['type'] ?? 2,
                    'cc_id' => $cc['id'],
                    'node_id' => $nodeId,
                ]);
        }
    }

    /**
     * 验证流程数据
     */
    protected function validateFlowData(array $flowData): void
    {
        if (empty($flowData['title'])) {
            throw new \InvalidArgumentException('审批流程标题不能为空');
        }

        if (empty($flowData['creator_id'])) {
            throw new \InvalidArgumentException('创建人ID不能为空');
        }

        if (empty($flowData['nodes'])) {
            throw new \InvalidArgumentException('审批节点不能为空');
        }
    }

    /**
     * 处理审批操作
     */
    public function processApproval(int $flowId, int $approverId, int $status, string $comment = ''): bool
    {
        if ($status === self::STATUS_REJECTED && empty($comment)) {
            throw new \InvalidArgumentException('拒绝时必须填写审批意见');
        }

        // 获取当前待审批节点
        $currentNode = $this->getCurrentNode($flowId);
        if (!$currentNode) {
            throw new \RuntimeException('没有找到待审批的节点');
        }

        // 检查审批人权限
        if (!$this->checkApproverPermission($currentNode['id'], $approverId)) {
            throw new \RuntimeException('无权审批此节点');
        }

        // 保存审批记录
        $this->saveApprovalRecord($flowId, $currentNode['id'], $approverId, $status, $comment);

        // 处理审批结果
        if ($status === self::STATUS_APPROVED) {
            return $this->processApproved($flowId, $currentNode);
        } else {
            return $this->processRejected($flowId);
        }
    }

    /**
     * 获取当前待审批节点
     */
    protected function getCurrentNode(int $flowId): ?array
    {
        return Db::name($this->config['db_prefix'].'nodes')
            ->where('flow_id', $flowId)
            ->order('sort', 'asc')
            ->find();
    }

    /**
     * 检查审批人权限
     */
    protected function checkApproverPermission(int $nodeId, int $approverId): bool
    {
        return Db::name($this->config['db_prefix'].'approvers')
            ->where('node_id', $nodeId)
            ->where('approver_id', $approverId)
            ->count() > 0;
    }

    /**
     * 保存审批记录
     */
    protected function saveApprovalRecord(int $flowId, int $nodeId, int $approverId, int $status, string $comment): void
    {
        Db::name($this->config['db_prefix'].'records')
            ->insert([
                'flow_id' => $flowId,
                'node_id' => $nodeId,
                'approver_id' => $approverId,
                'status' => $status,
                'comment' => $comment,
                'created_at' => time(),
            ]);
    }

    /**
     * 处理通过审批
     */
    protected function processApproved(int $flowId, array $currentNode): bool
    {
        // 检查是否还有后续节点
        $nextNode = $this->getNextNode($flowId, $currentNode['sort']);
        
        if ($nextNode) {
            // 还有后续节点，发送通知
            $this->sendNotification($flowId, $nextNode['id']);
            return true;
        } else {
            // 没有后续节点，审批完成
            return $this->completeFlow($flowId, self::STATUS_APPROVED);
        }
    }

    /**
     * 处理拒绝审批
     */
    protected function processRejected(int $flowId): bool
    {
        return $this->completeFlow($flowId, self::STATUS_REJECTED);
    }

    /**
     * 获取下一个审批节点
     */
    protected function getNextNode(int $flowId, int $currentSort): ?array
    {
        $nodes = Db::name($this->config['db_prefix'].'nodes')
            ->where('flow_id', $flowId)
            ->where('sort', '>', $currentSort)
            ->order('sort', 'asc')
            ->select();

        foreach ($nodes as $node) {
            if (empty($node['condition'])) {
                return $node;
            }

            // 评估条件表达式
            if ($this->evaluateCondition($flowId, $node['condition'])) {
                return $node;
            }
        }

        return null;
    }

    /**
     * 评估条件表达式
     */
    protected function evaluateCondition(int $flowId, string $condition): bool
    {
        // 这里应该实现实际的条件评估逻辑
        // 示例实现：简单返回true，实际项目应该解析条件表达式
        return true;
    }

    /**
     * 获取审批流程详情
     */
    public function getFlowDetails(int $flowId): array
    {
        $flow = Db::name($this->config['db_prefix'].'flow')
            ->where('id', $flowId)
            ->find();

        if (!$flow) {
            throw new \RuntimeException('审批流程不存在');
        }

        $nodes = Db::name($this->config['db_prefix'].'nodes')
            ->where('flow_id', $flowId)
            ->order('sort', 'asc')
            ->select();

        foreach ($nodes as &$node) {
            $node['approvers'] = Db::name($this->config['db_prefix'].'approvers')
                ->where('node_id', $node['id'])
                ->select();

            $node['records'] = Db::name($this->config['db_prefix'].'records')
                ->where('node_id', $node['id'])
                ->order('created_at', 'asc')
                ->select();
        }

        $flow['nodes'] = $nodes;
        $flow['cc'] = Db::name($this->config['db_prefix'].'cc')
            ->where('flow_id', $flowId)
            ->select();

        return $flow;
    }

    /**
     * 获取用户的待审批列表
     */
    public function getUserPendingApprovals(int $userId): array
    {
        return Db::name($this->config['db_prefix'].'flow f')
            ->join($this->config['db_prefix'].'nodes n', 'f.id = n.flow_id')
            ->join($this->config['db_prefix'].'approvers a', 'n.id = a.node_id')
            ->where('f.status', self::STATUS_PENDING)
            ->where(function($query) use ($userId) {
                $query->where('a.approver_id', $userId)
                    ->orWhere('a.approver_type', 1)
                    ->whereExists(function($query) use ($userId) {
                        // 这里应该实现标签匹配逻辑
                        // 示例中简单返回true，实际项目应该检查用户是否属于该标签
                        $query->whereRaw('1=1');
                    });
            })
            ->field('f.*, n.name as node_name')
            ->select();
    }

    /**
     * 获取用户发起的审批
     */
    public function getUserCreatedFlows(int $userId): array
    {
        return Db::name($this->config['db_prefix'].'flow')
            ->where('creator_id', $userId)
            ->order('created_at', 'desc')
            ->select();
    }

    /**
     * 取消审批流程
     */
    public function cancelFlow(int $flowId, int $userId): bool
    {
        $flow = Db::name($this->config['db_prefix'].'flow')
            ->where('id', $flowId)
            ->find();

        if (!$flow) {
            throw new \RuntimeException('审批流程不存在');
        }

        if ($flow['creator_id'] != $userId) {
            throw new \RuntimeException('只有创建者可以取消审批流程');
        }

        if ($flow['status'] != self::STATUS_PENDING) {
            throw new \RuntimeException('只有待审批的流程可以取消');
        }

        return Db::name($this->config['db_prefix'].'flow')
            ->where('id', $flowId)
            ->update([
                'status' => self::STATUS_CANCELLED,
                'updated_at' => time(),
            ]);
    }

    /**
     * 完成审批流程
     */
    protected function completeFlow(int $flowId, int $status): bool
    {
        $result = Db::name($this->config['db_prefix'].'flow')
            ->where('id', $flowId)
            ->update([
                'status' => $status,
                'updated_at' => time(),
            ]);

        // 发送最终通知
        $this->sendFinalNotification($flowId, $status);

        return $result > 0;
    }

    /**
     * 发送通知
     */
    protected function sendNotification(int $flowId, int $nodeId): void
    {
        if ($this->config['message_callback'] && is_callable($this->config['message_callback'])) {
            call_user_func($this->config['message_callback'], 'node_notify', [
                'flow_id' => $flowId,
                'node_id' => $nodeId,
            ]);
        }
    }

    /**
     * 发送最终通知
     */
    protected function sendFinalNotification(int $flowId, int $status): void
    {
        if ($this->config['message_callback'] && is_callable($this->config['message_callback'])) {
            call_user_func($this->config['message_callback'], 'flow_completed', [
                'flow_id' => $flowId,
                'status' => $status,
            ]);
        }
    }
}
